package cn.sylinx.horm.resource.io;

import java.lang.annotation.Annotation;
import java.util.HashSet;
import java.util.Set;

public class ClassScanner extends BaseResourceScanner {

    private final ClassFilter classFilter;

    public static Set<Class<?>> scanAllPackageByAnnotation(String packageName,
            Class<? extends Annotation> annotationClass) {
        return scanAllPackage(packageName, clazz -> clazz.isAnnotationPresent(annotationClass));
    }

    public static Set<Class<?>> scanPackageByAnnotation(String packageName,
            Class<? extends Annotation> annotationClass) {
        return scanPackage(packageName, clazz -> clazz.isAnnotationPresent(annotationClass));
    }

    public static Set<Class<?>> scanAllPackageBySuper(String packageName, Class<?> superClass) {
        return scanAllPackage(packageName, clazz -> superClass.isAssignableFrom(clazz) && !superClass.equals(clazz));
    }

    public static Set<Class<?>> scanPackageBySuper(String packageName, Class<?> superClass) {
        return scanPackage(packageName, clazz -> superClass.isAssignableFrom(clazz) && !superClass.equals(clazz));
    }

    public static Set<Class<?>> scanAllPackage() {
        return scanAllPackage("", null);
    }

    public static Set<Class<?>> scanPackage() {
        return scanPackage("", null);
    }

    public static Set<Class<?>> scanPackage(String packageName) {
        return scanPackage(packageName, null);
    }

    public static Set<Class<?>> scanAllPackage(String packageName, ClassFilter classFilter) {
        Set<Object> sets = new ClassScanner(packageName, classFilter).scan(true);
        Set<Class<?>> classSets = new HashSet<>();
        sets.forEach(c -> classSets.add((Class<?>) c));
        return classSets;
    }

    public static Set<Class<?>> scanPackage(String packageName, ClassFilter classFilter) {
        Set<Object> sets = new ClassScanner(packageName, classFilter).scan();
        Set<Class<?>> classSets = new HashSet<>();
        sets.forEach(c -> classSets.add((Class<?>) c));
        return classSets;
    }

    public ClassScanner() {
        this(null);
    }

    public ClassScanner(String packageName) {
        this(packageName, null);
    }

    public ClassScanner(String packageName, ClassFilter classFilter) {
        super(packageName);
        this.classFilter = classFilter;
    }

    @Override
    protected String getMatchedPostfix() {
        return ".class";
    }

    @Override
    protected boolean accept(String resoureName) {

        Class<?> clazz = loadClass(resoureName);
        if (null != clazz) {
            return classFilter == null || classFilter.accept(clazz);
        }
        return false;
    }

    @Override
    protected Object transform(String resoureName) {
        return loadClass(resoureName);
    }

    private Class<?> loadClass(String className) {

        ClassLoader loader = Resources.getClassLoader();

        Class<?> clazz = null;
        try {
            clazz = Class.forName(className, false, loader);
        } catch (Throwable e) {
            // do nothing
        }
        return clazz;
    }

}
